use std::str::FromStr;

use actix_web::{web, HttpResponse};
use wither::bson::oid::ObjectId;

use crate::{
    errors::cons_error,
    middlewares::authorize::{club_getters::default_getter, Authorize},
    models::{
        invite::InviteTokenModelDTO,
        user::{NormalUserModel, UserModelDTO, UserRole},
    },
    services::invite_service::InviteService,
    utils::authorize::{check_user_has_permission, check_user_has_permission_with_object_id},
    AppError, AppResult, PaginationQuery,
};

use super::OK_RESPONSE;
mod ctrl_struct {
    use chrono::{DateTime, Utc};

    use crate::models::invite::InviteTokenType;

    define_request_struct!(InviteUserToClubCtrlReq {
        club_id: String,
        user_id: String
    });

    define_request_struct!(CreateInviteTokenCtrlBodyReq{
        club_id:String,
        max_count:u64,
        expired_time:DateTime<Utc>,
        invite_type:InviteTokenType
    });
    define_filter_request_struct!(SearchAllInviteTokensInClubReq { club_id: String });
    define_request_struct!(DeleteInviteTokenReq { invite_id: String });
    define_request_struct!(FindOneInviteTokenReq { invite_id: String });
    define_filter_request_struct!(SearchAllInviteTokenReq {});
    define_request_struct!(UseInviteTokenReq { invite_id: String });
}

/// invite_user_to_club
/// 邀请用户加入社团
async fn invite_user_to_club(
    invite_service: web::Data<InviteService>,
    user: NormalUserModel,
    req: web::Json<ctrl_struct::InviteUserToClubCtrlReq>,
) -> AppResult<HttpResponse> {
    let (club_id, target_id) = (
        ObjectId::from_str(&req.club_id)?,
        ObjectId::from_str(&req.user_id)?,
    );

    // 检查是否拥有对社团人员的操作权限
    if !check_user_has_permission(
        &user.clone().into(),
        club_id.clone().to_string(),
        UserRole::LeaderOrAdmin as u8,
    ) {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    if !invite_service
        .check_user_in_club_invite_token(club_id, user.id, None)
        .await?
    {
        return Err(AppError::ConsError(
            cons_error::MSG_USER_SHOULD_IN_INVITE_TOKEN,
        ));
    }

    let res = invite_service
        .invite_user_to_club(user, target_id, club_id)
        .await?;

    Ok(HttpResponse::Ok().json(UserModelDTO::from(res)))
}
async fn create_invite_token(
    invite_service: web::Data<InviteService>,
    user: NormalUserModel,
    body_req: web::Json<ctrl_struct::CreateInviteTokenCtrlBodyReq>,
) -> AppResult<HttpResponse> {
    let club_id = ObjectId::from_str(&body_req.club_id)?;
    if !check_user_has_permission_with_object_id(user.clone(), club_id, UserRole::LeaderOrAdmin) {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let token = invite_service
        .create_invite_token(
            user,
            body_req.max_count,
            body_req.expired_time.to_owned(),
            body_req.invite_type,
            club_id,
        )
        .await?;

    Ok(HttpResponse::Ok().json(InviteTokenModelDTO::from(token)))
}

async fn search_all_invite_tokens_in_club(
    invite_service: web::Data<InviteService>,
    user: NormalUserModel,
    where_value: web::Json<ctrl_struct::SearchAllInviteTokensInClubReq>,
    query: web::Query<PaginationQuery>,
) -> AppResult<HttpResponse> {
    let club_id = ObjectId::from_str(&where_value.club_id)?;
    if !check_user_has_permission(
        &user.into(),
        club_id.clone().to_string(),
        UserRole::LeaderOrAdmin as u8,
    ) {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let mut filter = where_value
        .get_filter_as_document_use_default()
        .unwrap_or_default();
    filter.insert("club_id", club_id);
    let res = invite_service
        .find_all_invite_tokens(filter, &query)
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<InviteTokenModelDTO>()))
}

async fn delete_invite_token(
    invite_service: web::Data<InviteService>,
    user: NormalUserModel,
    req: web::Json<ctrl_struct::DeleteInviteTokenReq>,
) -> AppResult<HttpResponse> {
    let invite_id = ObjectId::from_str(&req.invite_id)?;
    let invite = invite_service
        .find_one_invite_token(invite_id, None)
        .await?;
    if !check_user_has_permission(
        &user.into(),
        invite.club_id.clone().to_string(),
        UserRole::LeaderOrAdmin as u8,
    ) {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    invite_service.delete_invite_token(invite_id).await?;
    Ok(HttpResponse::Ok().json(OK_RESPONSE))
}

async fn find_one_invite_token(
    invite_service: web::Data<InviteService>,
    req: web::Json<ctrl_struct::FindOneInviteTokenReq>,
) -> AppResult<HttpResponse> {
    let invite_id = ObjectId::from_str(&req.invite_id)?;
    let invite = invite_service
        .find_one_invite_token(invite_id, None)
        .await?;
    Ok(HttpResponse::Ok().json(InviteTokenModelDTO::from(invite)))
}

async fn search_all_invite_tokens(
    invite_service: web::Data<InviteService>,
    where_value: web::Json<ctrl_struct::SearchAllInviteTokenReq>,
    query: web::Query<PaginationQuery>,
) -> AppResult<HttpResponse> {
    let filter = where_value
        .get_filter_as_document_use_default()
        .unwrap_or_default();

    let res = invite_service
        .find_all_invite_tokens(filter, &query)
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<InviteTokenModelDTO>()))
}

async fn use_invite_token(
    invite_service: web::Data<InviteService>,
    user: NormalUserModel,
    req: web::Json<ctrl_struct::UseInviteTokenReq>,
) -> AppResult<HttpResponse> {
    let invite_id = ObjectId::from_str(&req.invite_id)?;
    let res = invite_service
        .add_user_to_invite_token(invite_id, user)
        .await?;
    Ok(HttpResponse::Ok().json(InviteTokenModelDTO::from(res)))
}
pub fn configure_invite_controller(config: &mut web::ServiceConfig) {
    config
        .route("/to_club", web::post().to(invite_user_to_club))
        .route("/use", web::post().to(use_invite_token))
        .route("/delete", web::post().to(delete_invite_token))
        .route("/detail", web::post().to(find_one_invite_token))
        .route("/create", web::post().to(create_invite_token))
        .service(
            web::resource("/search_in_club")
                .route(web::post().to(search_all_invite_tokens_in_club)),
        )
        .service(
            web::resource("/search")
                .route(web::post().to(search_all_invite_tokens))
                .wrap(Authorize::new(UserRole::Admin, default_getter)),
        );
}
